from typing import Dict, Any, List
import time

from kubernetes import client
from kubernetes.client.rest import ApiException
from kubernetes.client import (
    V1Deployment, V1Service, V1ObjectMeta, V1LabelSelector, V1PodTemplateSpec,
    V1EnvVarSource, V1SecretKeySelector, V1ConfigMapKeySelector,
    V1Container, V1ContainerPort, V1EnvVar, V1ResourceRequirements,
    V1Probe, V1HTTPGetAction, V1ServicePort, V1ServiceSpec,
    V1DeploymentSpec, V1PodSpec, V1ReplicaSetSpec,
    V1Volume, V1VolumeMount, V1ConfigMapVolumeSource
)

from .api import Website, WebsiteSpec, API_VERSION, KIND


class WebsiteResourceType:
    """Website资源类型"""
    DEPLOYMENT = "deployment"
    SERVICE = "service"


class WebsiteResourceManager:
    """Website资源管理器"""

    def __init__(self, namespace: str, logger):
        self.namespace = namespace
        self.logger = logger
        self.core_api = client.CoreV1Api()
        self.apps_api = client.AppsV1Api()

    def get_resource_name(self, website_name: str) -> str:
        """生成Website相关的资源名称"""
        return f"website-{website_name}"

    def normalize_name(self, name: str) -> str:
        """规范化名称以符合Kubernetes RFC 1123标签规范
        
        Args:
            name: 原始名称
            
        Returns:
            str: 规范化后的名称
        """
        import re
        import unicodedata
        
        # 转换为小写
        normalized = name.lower()
        
        # 移除重音符号和特殊字符
        normalized = unicodedata.normalize('NFD', normalized)
        normalized = ''.join(c for c in normalized if unicodedata.category(c) != 'Mn')
        
        # 只保留字母、数字和连字符
        normalized = re.sub(r'[^a-z0-9-]', '-', normalized)
        
        # 移除连续的连字符
        normalized = re.sub(r'-+', '-', normalized)
        
        # 移除开头和结尾的连字符
        normalized = normalized.strip('-')
        
        # 确保以字母或数字开头和结尾
        if normalized and not normalized[0].isalnum():
            normalized = 'app-' + normalized
        if normalized and not normalized[-1].isalnum():
            normalized = normalized + '-app'
            
        # 如果为空或只有连字符，使用默认名称
        if not normalized or normalized == '-':
            normalized = 'website-app'
            
        # 限制长度（Kubernetes标签最大63个字符）
        if len(normalized) > 63:
            normalized = normalized[:63].rstrip('-')
            
        return normalized

    def create_deployment(self, website: Website) -> V1Deployment:
        """创建Deployment资源"""
        spec = website.get_spec()
        metadata = website.metadata

        # 构建容器配置
        container = V1Container(
            name=self.normalize_name(spec.name),
            image=f"{spec.image.repository}:{spec.image.tag}",
            image_pull_policy=spec.image.pull_policy.value,
            ports=[V1ContainerPort(container_port=spec.service.target_port)],
            resources=V1ResourceRequirements(
                requests=spec.resources.requests,
                limits=spec.resources.limits
            )
        )

        # 如果有自定义HTML内容，添加init container
        init_containers = []
        if spec.custom_html:
            init_container = V1Container(
                name="setup-html",
                image="busybox:1.35",
                command=["/bin/sh"],
                args=["-c", f"echo '{spec.custom_html}' > /usr/share/nginx/html/index.html"],
                volume_mounts=[
                    V1VolumeMount(
                        name="nginx-html",
                        mount_path="/usr/share/nginx/html"
                    )
                ]
            )
            init_containers.append(init_container)
            
            # 为nginx容器添加volume mount
            container.volume_mounts = [
                V1VolumeMount(
                    name="nginx-html",
                    mount_path="/usr/share/nginx/html"
                )
            ]

        # 添加环境变量
        if spec.env:
            env_vars = []
            for env_item in spec.env:
                if env_item.value:
                    env_vars.append(V1EnvVar(name=env_item.name, value=env_item.value))
                elif env_item.value_from:
                    # 处理从Secret或ConfigMap获取的值
                    value_from = env_item.value_from
                    if 'secretKeyRef' in value_from:
                        secret_ref = value_from['secretKeyRef']
                        env_vars.append(V1EnvVar(
                            name=env_item.name,
                            value_from=V1EnvVarSource(
                                secret_key_ref=V1SecretKeySelector(
                                    name=secret_ref['name'],
                                    key=secret_ref['key']
                                )
                            )
                        ))
                    elif 'configMapKeyRef' in value_from:
                        config_ref = value_from['configMapKeyRef']
                        env_vars.append(V1EnvVar(
                            name=env_item.name,
                            value_from=V1EnvVarSource(
                                config_map_key_ref=V1ConfigMapKeySelector(
                                    name=config_ref['name'],
                                    key=config_ref['key']
                                )
                            )
                        ))
            container.env = env_vars

        # 添加健康检查
        if spec.health_check.enabled:
            container.liveness_probe = V1Probe(
                http_get=V1HTTPGetAction(
                    path=spec.health_check.path,
                    port=spec.health_check.port
                ),
                initial_delay_seconds=spec.health_check.initial_delay_seconds,
                period_seconds=spec.health_check.period_seconds,
                timeout_seconds=spec.health_check.timeout_seconds,
                failure_threshold=spec.health_check.failure_threshold,
                success_threshold=spec.health_check.success_threshold
            )

        # 构建Deployment
        deployment_name = self.get_resource_name(metadata['name'])
        deployment = V1Deployment(
            metadata=V1ObjectMeta(
                name=deployment_name,
                namespace=self.namespace,
                labels={
                    'app': deployment_name,
                    'website': metadata['name'],
                    'managed-by': 'website-controller'
                },
                owner_references=[{
                    'apiVersion': API_VERSION,
                    'kind': KIND,
                    'name': metadata['name'],
                    'uid': metadata['uid'],
                    'controller': True,
                    'blockOwnerDeletion': True
                }]
            ),
            spec=V1DeploymentSpec(
                replicas=spec.replicas,
                selector=V1LabelSelector(
                    match_labels={'app': deployment_name}
                ),
                template=V1PodTemplateSpec(
                    metadata=V1ObjectMeta(
                        labels={'app': deployment_name}
                    ),
                    spec=V1PodSpec(
                        init_containers=init_containers,
                        containers=[container],
                        volumes=[
                            V1Volume(
                                name="nginx-html",
                                empty_dir={}
                            )
                        ] if spec.custom_html else []
                    )
                )
            )
        )

        return deployment

    def create_service(self, website: Website) -> V1Service:
        """创建Service资源"""
        spec = website.get_spec()
        metadata = website.metadata

        service_name = self.get_resource_name(metadata['name'])
        service = V1Service(
            metadata=V1ObjectMeta(
                name=service_name,
                namespace=self.namespace,
                labels={
                    'app': service_name,
                    'website': metadata['name'],
                    'managed-by': 'website-controller'
                },
                owner_references=[{
                    'apiVersion': API_VERSION,
                    'kind': KIND,
                    'name': metadata['name'],
                    'uid': metadata['uid'],
                    'controller': True,
                    'blockOwnerDeletion': True
                }]
            ),
            spec=V1ServiceSpec(
                type=spec.service.type.value,
                ports=[V1ServicePort(
                    port=spec.service.port,
                    target_port=spec.service.target_port,
                    protocol='TCP'
                )],
                selector={'app': service_name}
            )
        )

        return service

    def create_resource(self, resource_type: str, website: Website) -> None:
        """创建资源"""
        if resource_type == WebsiteResourceType.DEPLOYMENT:
            deployment = self.create_deployment(website)
            try:
                self.apps_api.create_namespaced_deployment(
                    namespace=self.namespace,
                    body=deployment
                )
                self.logger.info(f"Created Deployment {deployment.metadata.name}")
            except ApiException as e:
                if e.status == 409:  # Already exists
                    self.logger.info(f"Deployment {deployment.metadata.name} already exists")
                else:
                    raise
        elif resource_type == WebsiteResourceType.SERVICE:
            service = self.create_service(website)
            try:
                self.core_api.create_namespaced_service(
                    namespace=self.namespace,
                    body=service
                )
                self.logger.info(f"Created Service {service.metadata.name}")
            except ApiException as e:
                if e.status == 409:  # Already exists
                    self.logger.info(f"Service {service.metadata.name} already exists")
                else:
                    raise

    def update_resource(self, resource_type: str, website: Website) -> None:
        """更新资源"""
        if resource_type == WebsiteResourceType.DEPLOYMENT:
            deployment = self.create_deployment(website)
            try:
                self.apps_api.patch_namespaced_deployment(
                    name=deployment.metadata.name,
                    namespace=self.namespace,
                    body=deployment
                )
                self.logger.info(f"Updated Deployment {deployment.metadata.name}")
            except ApiException as e:
                if e.status == 404:
                    # Deployment不存在，创建它
                    self.apps_api.create_namespaced_deployment(
                        namespace=self.namespace,
                        body=deployment
                    )
                    self.logger.info(f"Created Deployment {deployment.metadata.name}")
                else:
                    raise
        elif resource_type == WebsiteResourceType.SERVICE:
            service = self.create_service(website)
            try:
                self.core_api.patch_namespaced_service(
                    name=service.metadata.name,
                    namespace=self.namespace,
                    body=service
                )
                self.logger.info(f"Updated Service {service.metadata.name}")
            except ApiException as e:
                if e.status == 404:
                    # Service不存在，创建它
                    self.core_api.create_namespaced_service(
                        namespace=self.namespace,
                        body=service
                    )
                    self.logger.info(f"Created Service {service.metadata.name}")
                else:
                    raise

    def delete_resource(self, resource_type: str, website_name: str) -> None:
        """删除资源"""
        resource_name = self.get_resource_name(website_name)
        
        if resource_type == WebsiteResourceType.DEPLOYMENT:
            try:
                self.apps_api.delete_namespaced_deployment(
                    name=resource_name,
                    namespace=self.namespace
                )
                self.logger.info(f"Deleted Deployment {resource_name}")
            except ApiException as e:
                if e.status == 404:
                    self.logger.info(f"Deployment {resource_name} already deleted")
                else:
                    raise
        elif resource_type == WebsiteResourceType.SERVICE:
            try:
                self.core_api.delete_namespaced_service(
                    name=resource_name,
                    namespace=self.namespace
                )
                self.logger.info(f"Deleted Service {resource_name}")
            except ApiException as e:
                if e.status == 404:
                    self.logger.info(f"Service {resource_name} already deleted")
                else:
                    raise

    def wait_for_deployment_ready(self, website_name: str, timeout: int = 60) -> bool:
        """等待Deployment就绪
        
        Args:
            website_name: Website名称
            timeout: 超时时间（秒）
            
        Returns:
            bool: True如果Deployment就绪，False如果超时
        """
        deployment_name = self.get_resource_name(website_name)
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            try:
                deployment = self.apps_api.read_namespaced_deployment(
                    name=deployment_name,
                    namespace=self.namespace
                )
                
                if (deployment.status.ready_replicas and 
                    deployment.status.ready_replicas >= deployment.spec.replicas):
                    self.logger.info(f"Deployment {deployment_name} is ready")
                    return True
                    
            except ApiException as e:
                if e.status == 404:
                    self.logger.debug(f"Deployment {deployment_name} not found yet, waiting...")
                else:
                    self.logger.error(f"Error checking deployment status: {e}")
                    return False
            
            time.sleep(2)  # 等待2秒后再次检查
        
        self.logger.warning(f"Timeout waiting for deployment {deployment_name} to be ready")
        return False

    def check_deployment_exists(self, website_name: str) -> bool:
        """检查Deployment是否存在
        
        Args:
            website_name: Website名称
            
        Returns:
            bool: True如果Deployment存在，False否则
        """
        deployment_name = self.get_resource_name(website_name)
        try:
            self.apps_api.read_namespaced_deployment(
                name=deployment_name,
                namespace=self.namespace
            )
            return True
        except ApiException as e:
            if e.status == 404:
                return False
            raise

    def check_service_exists(self, website_name: str) -> bool:
        """检查Service是否存在
        
        Args:
            website_name: Website名称
            
        Returns:
            bool: True如果Service存在，False否则
        """
        service_name = self.get_resource_name(website_name)
        try:
            self.core_api.read_namespaced_service(
                name=service_name,
                namespace=self.namespace
            )
            return True
        except ApiException as e:
            if e.status == 404:
                return False
            raise
